package com.util;

/**
 * @author cxt
 * @date 2022/4/13
 * 雪花算法实现全局唯一id
 */
public class SnowFlake {

    /**
     * 组成
     */
    // 最高位 0
    // 时间戳(系统自带算法实现)
    //初始时间戳
    private final long twepoch = 1288834974657L;

    // 机房id
    private long computerRoomId;

    // 机器id
    private long machineId;

    // 序列号
    private long sequence = 8L;

    /**
     * 占用bit
     */
    // 时间戳 41bit

    // 机房id 5bit
    private final long computerRoomBitCnt = 5L;

    // 机器id 5bit
    private final long machineBitCnt = 5L;

    // 序列号 12bit
    private final long sequenceBitCnt = 12L;

    /**
     * 位移的位数
     */
    // 机器id位移 = 序列号占用
    private final long machineIdShift = sequenceBitCnt;

    // 机房id位移 = 机器位移 + 自身所占用
    private final long computerRoomIdShift = machineIdShift + computerRoomBitCnt;

    // 时间戳 = 机房位移 + 机房占用
    private final long timeStampShift = computerRoomIdShift + computerRoomBitCnt;

    /**
     * 一些聚合信息
     */
    // 支持机房最大id数 2^5 - 1 = 31
    private final long maxComputerRoomId = -1 ^ (-1 << computerRoomBitCnt);
    // 支持最大的机器id数
    private final long maxMachineId = -1 ^ (-1 << machineBitCnt);
    // 支持最大的序列号
    private final long sequenceMask = -1 ^ (-1 << sequenceBitCnt);
    // 上一次生成的时间戳
    private long lastTimeStamp = -1L;

    public SnowFlake(long computerRoomId, long machineId) {
        if (computerRoomId < 0 || computerRoomId > maxComputerRoomId) {
            throw new IllegalArgumentException("computerRoomId out of range");
        }
        if (machineId < 0 || machineId > maxMachineId) {
            throw new IllegalArgumentException("computerRoomId out of range");
        }
        this.computerRoomId = computerRoomId;
        this.machineId = machineId;
    }

    /**
     * 毫秒级的时间戳
     */
    private long getCurrentTimeMillis() {
        return System.currentTimeMillis();
    }

    public synchronized long getNextId() {
        long currentTimeMillis = getCurrentTimeMillis();
        // 判断当前时间是不是等于上一次的时间
        if (currentTimeMillis == lastTimeStamp) {
            sequence = (sequence + 1) & sequenceMask; // 取模 这样为了更快
            if (sequence == 0) {
                //  代表该毫秒级所能生成的唯一id已经全部用完，等待下一毫秒
                currentTimeMillis = getNextTimeMillis();
            }
        } else {
            // 不是同一个毫秒
            sequence = 0;
        }
        lastTimeStamp = currentTimeMillis;

        // 唯一id
        return ((currentTimeMillis - twepoch) << timeStampShift) | (computerRoomId << computerRoomIdShift) | (maxMachineId << machineIdShift) | sequence;
    }

    /**
     * 下一秒时间戳
     */
    private long getNextTimeMillis() {
        long currentTimeMillis = getCurrentTimeMillis();
        while (currentTimeMillis <= lastTimeStamp) {
            currentTimeMillis = getCurrentTimeMillis();
        }
        return currentTimeMillis;
    }

    // 测试
    public static void main(String[] args) {
        SnowFlake flake = new SnowFlake(1, 2);
        long nextId = flake.getNextId();
        System.out.println(nextId);
    }
}
